home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Development / ncurses-5.3 / ncurses / tinfo / comp_scan.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-27  |  20.6 KB  |  832 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*
  35.  *    comp_scan.c --- Lexical scanner for terminfo compiler.
  36.  *
  37.  *    _nc_reset_input()
  38.  *    _nc_get_token()
  39.  *    _nc_panic_mode()
  40.  *    int _nc_syntax;
  41.  *    int _nc_curr_line;
  42.  *    long _nc_curr_file_pos;
  43.  *    long _nc_comment_start;
  44.  *    long _nc_comment_end;
  45.  */
  46.  
  47. #include <curses.priv.h>
  48.  
  49. #include <ctype.h>
  50. #include <term_entry.h>
  51. #include <tic.h>
  52.  
  53. MODULE_ID("$Id: comp_scan.c,v 1.61 2002/09/07 20:04:09 tom Exp $")
  54.  
  55. /*
  56.  * Maximum length of string capability we'll accept before raising an error.
  57.  * Yes, there is a real capability in /etc/termcap this long, an "is".
  58.  */
  59. #define MAXCAPLEN    600
  60.  
  61. #define iswhite(ch)    (ch == ' '  ||  ch == '\t')
  62.  
  63. NCURSES_EXPORT_VAR(int)
  64. _nc_syntax = 0;            /* termcap or terminfo? */
  65. NCURSES_EXPORT_VAR(long)
  66. _nc_curr_file_pos = 0;        /* file offset of current line */
  67. NCURSES_EXPORT_VAR(long)
  68. _nc_comment_start = 0;        /* start of comment range before name */
  69. NCURSES_EXPORT_VAR(long)
  70. _nc_comment_end = 0;        /* end of comment range before name */
  71. NCURSES_EXPORT_VAR(long)
  72. _nc_start_line = 0;        /* start line of current entry */
  73.  
  74. NCURSES_EXPORT_VAR(struct token)
  75. _nc_curr_token =
  76. {
  77.     0, 0, 0
  78. };
  79.  
  80. /*****************************************************************************
  81.  *
  82.  * Token-grabbing machinery
  83.  *
  84.  *****************************************************************************/
  85.  
  86. static bool first_column;    /* See 'next_char()' below */
  87. static char separator;        /* capability separator */
  88. static int pushtype;        /* type of pushback token */
  89. static char *pushname;
  90.  
  91. #if NCURSES_EXT_FUNCS
  92. NCURSES_EXPORT_VAR(bool)
  93. _nc_disable_period = FALSE;    /* used by tic -a option */
  94. #endif
  95.  
  96. static int last_char(void);
  97. static int next_char(void);
  98. static long stream_pos(void);
  99. static bool end_of_stream(void);
  100. static void push_back(char c);
  101.  
  102. /* Assume we may be looking at a termcap-style continuation */
  103. static inline int
  104. eat_escaped_newline(int ch)
  105. {
  106.     if (ch == '\\')
  107.     while ((ch = next_char()) == '\n' || iswhite(ch))
  108.         continue;
  109.     return ch;
  110. }
  111.  
  112. /*
  113.  *    int
  114.  *    get_token()
  115.  *
  116.  *    Scans the input for the next token, storing the specifics in the
  117.  *    global structure 'curr_token' and returning one of the following:
  118.  *
  119.  *        NAMES        A line beginning in column 1.  'name'
  120.  *                will be set to point to everything up to but
  121.  *                not including the first separator on the line.
  122.  *        BOOLEAN        An entry consisting of a name followed by
  123.  *                a separator.  'name' will be set to point to
  124.  *                the name of the capability.
  125.  *        NUMBER        An entry of the form
  126.  *                    name#digits,
  127.  *                'name' will be set to point to the capability
  128.  *                name and 'valnumber' to the number given.
  129.  *        STRING        An entry of the form
  130.  *                    name=characters,
  131.  *                'name' is set to the capability name and
  132.  *                'valstring' to the string of characters, with
  133.  *                input translations done.
  134.  *        CANCEL        An entry of the form
  135.  *                    name@,
  136.  *                'name' is set to the capability name and
  137.  *                'valnumber' to -1.
  138.  *        EOF        The end of the file has been reached.
  139.  *
  140.  *    A `separator' is either a comma or a semicolon, depending on whether
  141.  *    we are in termcap or terminfo mode.
  142.  *
  143.  */
  144.  
  145. NCURSES_EXPORT(int)
  146. _nc_get_token(bool silent)
  147. {
  148.     static const char terminfo_punct[] = "@%&*!#";
  149.     static char *buffer;
  150.  
  151.     char *numchk;
  152.     char *ptr;
  153.     char numbuf[80];
  154.     int ch;
  155.     int dot_flag = FALSE;
  156.     int type;
  157.     long number;
  158.     long token_start;
  159.     unsigned found;
  160.  
  161.     if (pushtype != NO_PUSHBACK) {
  162.     int retval = pushtype;
  163.  
  164.     _nc_set_type(pushname != 0 ? pushname : "");
  165.     DEBUG(3, ("pushed-back token: `%s', class %d",
  166.           _nc_curr_token.tk_name, pushtype));
  167.  
  168.     pushtype = NO_PUSHBACK;
  169.     if (pushname != 0)
  170.         pushname[0] = '\0';
  171.  
  172.     /* currtok wasn't altered by _nc_push_token() */
  173.     return (retval);
  174.     }
  175.  
  176.     if (end_of_stream())
  177.     return (EOF);
  178.  
  179.   start_token:
  180.     token_start = stream_pos();
  181.     while ((ch = next_char()) == '\n' || iswhite(ch))
  182.     continue;
  183.  
  184.     ch = eat_escaped_newline(ch);
  185.  
  186.     if (ch == EOF)
  187.     type = EOF;
  188.     else {
  189.     /* if this is a termcap entry, skip a leading separator */
  190.     if (separator == ':' && ch == ':')
  191.         ch = next_char();
  192.  
  193.     if (ch == '.'
  194. #if NCURSES_EXT_FUNCS
  195.         && !_nc_disable_period
  196. #endif
  197.         ) {
  198.         dot_flag = TRUE;
  199.         DEBUG(8, ("dot-flag set"));
  200.  
  201.         while ((ch = next_char()) == '.' || iswhite(ch))
  202.         continue;
  203.     }
  204.  
  205.     if (ch == EOF) {
  206.         type = EOF;
  207.         goto end_of_token;
  208.     }
  209.  
  210.     /* have to make some punctuation chars legal for terminfo */
  211.     if (!isalnum(ch)
  212. #if NCURSES_EXT_FUNCS
  213.         && !(ch == '.' && _nc_disable_period)
  214. #endif
  215.         && !strchr(terminfo_punct, (char) ch)) {
  216.         if (!silent)
  217.         _nc_warning("Illegal character (expected alphanumeric or %s) - %s",
  218.                 terminfo_punct, unctrl((chtype) ch));
  219.         _nc_panic_mode(separator);
  220.         goto start_token;
  221.     }
  222.  
  223.     if (buffer == 0)
  224.         buffer = typeMalloc(char, MAX_ENTRY_SIZE);
  225.  
  226.     ptr = buffer;
  227.     *(ptr++) = ch;
  228.  
  229.     if (first_column) {
  230.         char *desc;
  231.  
  232.         _nc_comment_start = token_start;
  233.         _nc_comment_end = _nc_curr_file_pos;
  234.         _nc_start_line = _nc_curr_line;
  235.  
  236.         _nc_syntax = ERR;
  237.         while ((ch = next_char()) != '\n') {
  238.         if (ch == EOF)
  239.             _nc_err_abort(MSG_NO_MEMORY);
  240.         else if (ch == ':' && last_char() != ',') {
  241.             _nc_syntax = SYN_TERMCAP;
  242.             separator = ':';
  243.             break;
  244.         } else if (ch == ',') {
  245.             _nc_syntax = SYN_TERMINFO;
  246.             separator = ',';
  247.             /*
  248.              * Fall-through here is not an accident.  The idea is that
  249.              * if we see a comma, we figure this is terminfo unless we
  250.              * subsequently run into a colon -- but we don't stop
  251.              * looking for that colon until hitting a newline.  This
  252.              * allows commas to be embedded in description fields of
  253.              * either syntax.
  254.              */
  255.             /* FALLTHRU */
  256.         } else
  257.             ch = eat_escaped_newline(ch);
  258.  
  259.         *ptr++ = ch;
  260.         }
  261.         ptr[0] = '\0';
  262.         if (_nc_syntax == ERR) {
  263.         /*
  264.          * Grrr...what we ought to do here is barf, complaining that
  265.          * the entry is malformed.  But because a couple of name fields
  266.          * in the 8.2 termcap file end with |\, we just have to assume
  267.          * it's termcap syntax.
  268.          */
  269.         _nc_syntax = SYN_TERMCAP;
  270.         separator = ':';
  271.         } else if (_nc_syntax == SYN_TERMINFO) {
  272.         /* throw away trailing /, *$/ */
  273.         for (--ptr; iswhite(*ptr) || *ptr == ','; ptr--)
  274.             continue;
  275.         ptr[1] = '\0';
  276.         }
  277.  
  278.         /*
  279.          * This is the soonest we have the terminal name fetched.  Set up
  280.          * for following warning messages.
  281.          */
  282.         ptr = strchr(buffer, '|');
  283.         if (ptr == (char *) NULL)
  284.         ptr = buffer + strlen(buffer);
  285.         ch = *ptr;
  286.         *ptr = '\0';
  287.         _nc_set_type(buffer);
  288.         *ptr = ch;
  289.  
  290.         /*
  291.          * Compute the boundary between the aliases and the description
  292.          * field for syntax-checking purposes.
  293.          */
  294.         desc = strrchr(buffer, '|');
  295.         if (!silent && desc) {
  296.         if (*desc == '\0')
  297.             _nc_warning("empty longname field");
  298.         else if (strchr(desc, ' ') == (char *) NULL)
  299.             _nc_warning("older tic versions may treat the description field as an alias");
  300.         }
  301.         if (!desc)
  302.         desc = buffer + strlen(buffer);
  303.  
  304.         /*
  305.          * Whitespace in a name field other than the long name can confuse
  306.          * rdist and some termcap tools.  Slashes are a no-no.  Other
  307.          * special characters can be dangerous due to shell expansion.
  308.          */
  309.         for (ptr = buffer; ptr < desc; ptr++) {
  310.         if (isspace(UChar(*ptr))) {
  311.             if (!silent)
  312.             _nc_warning("whitespace in name or alias field");
  313.             break;
  314.         } else if (*ptr == '/') {
  315.             if (!silent)
  316.             _nc_warning("slashes aren't allowed in names or aliases");
  317.             break;
  318.         } else if (strchr("$[]!*?", *ptr)) {
  319.             if (!silent)
  320.             _nc_warning("dubious character `%c' in name or alias field", *ptr);
  321.             break;
  322.         }
  323.         }
  324.  
  325.         ptr = buffer;
  326.  
  327.         _nc_curr_token.tk_name = buffer;
  328.         type = NAMES;
  329.     } else {
  330.         while ((ch = next_char()) != EOF) {
  331.         if (!isalnum(ch)) {
  332.             if (_nc_syntax == SYN_TERMINFO) {
  333.             if (ch != '_')
  334.                 break;
  335.             } else {    /* allow ';' for "k;" */
  336.             if (ch != ';')
  337.                 break;
  338.             }
  339.         }
  340.         *(ptr++) = ch;
  341.         }
  342.  
  343.         *ptr++ = '\0';
  344.         switch (ch) {
  345.         case ',':
  346.         case ':':
  347.         if (ch != separator)
  348.             _nc_err_abort("Separator inconsistent with syntax");
  349.         _nc_curr_token.tk_name = buffer;
  350.         type = BOOLEAN;
  351.         break;
  352.         case '@':
  353.         if ((ch = next_char()) != separator && !silent)
  354.             _nc_warning("Missing separator after `%s', have %s",
  355.                 buffer, unctrl((chtype) ch));
  356.         _nc_curr_token.tk_name = buffer;
  357.         type = CANCEL;
  358.         break;
  359.  
  360.         case '#':
  361.         found = 0;
  362.         while (isalnum(ch = next_char())) {
  363.             numbuf[found++] = ch;
  364.             if (found >= sizeof(numbuf) - 1)
  365.             break;
  366.         }
  367.         numbuf[found] = '\0';
  368.         number = strtol(numbuf, &numchk, 0);
  369.         if (!silent) {
  370.             if (numchk == numbuf)
  371.             _nc_warning("no value given for `%s'", buffer);
  372.             if ((*numchk != '\0') || (ch != separator))
  373.             _nc_warning("Missing separator");
  374.         }
  375.         _nc_curr_token.tk_name = buffer;
  376.         _nc_curr_token.tk_valnumber = number;
  377.         type = NUMBER;
  378.         break;
  379.  
  380.         case '=':
  381.         ch = _nc_trans_string(ptr, buffer + MAX_ENTRY_SIZE);
  382.         if (!silent && ch != separator)
  383.             _nc_warning("Missing separator");
  384.         _nc_curr_token.tk_name = buffer;
  385.         _nc_curr_token.tk_valstring = ptr;
  386.         type = STRING;
  387.         break;
  388.  
  389.         case EOF:
  390.         type = EOF;
  391.         break;
  392.         default:
  393.         /* just to get rid of the compiler warning */
  394.         type = UNDEF;
  395.         if (!silent)
  396.             _nc_warning("Illegal character - %s", unctrl((chtype) ch));
  397.         }
  398.     }            /* end else (first_column == FALSE) */
  399.     }                /* end else (ch != EOF) */
  400.  
  401.   end_of_token:
  402.  
  403. #ifdef TRACE
  404.     if (dot_flag == TRUE)
  405.     DEBUG(8, ("Commented out "));
  406.  
  407.     if (_nc_tracing >= DEBUG_LEVEL(7)) {
  408.     switch (type) {
  409.     case BOOLEAN:
  410.         _tracef("Token: Boolean; name='%s'",
  411.             _nc_curr_token.tk_name);
  412.         break;
  413.  
  414.     case NUMBER:
  415.         _tracef("Token: Number;  name='%s', value=%d",
  416.             _nc_curr_token.tk_name,
  417.             _nc_curr_token.tk_valnumber);
  418.         break;
  419.  
  420.     case STRING:
  421.         _tracef("Token: String;  name='%s', value=%s",
  422.             _nc_curr_token.tk_name,
  423.             _nc_visbuf(_nc_curr_token.tk_valstring));
  424.         break;
  425.  
  426.     case CANCEL:
  427.         _tracef("Token: Cancel; name='%s'",
  428.             _nc_curr_token.tk_name);
  429.         break;
  430.  
  431.     case NAMES:
  432.  
  433.         _tracef("Token: Names; value='%s'",
  434.             _nc_curr_token.tk_name);
  435.         break;
  436.  
  437.     case EOF:
  438.         _tracef("Token: End of file");
  439.         break;
  440.  
  441.     default:
  442.         _nc_warning("Bad token type");
  443.     }
  444.     }
  445. #endif
  446.  
  447.     if (dot_flag == TRUE)    /* if commented out, use the next one */
  448.     type = _nc_get_token(silent);
  449.  
  450.     DEBUG(3, ("token: `%s', class %d",
  451.           _nc_curr_token.tk_name != 0 ? _nc_curr_token.tk_name :
  452.           "<null>",
  453.           type));
  454.  
  455.     return (type);
  456. }
  457.  
  458. /*
  459.  *    char
  460.  *    trans_string(ptr)
  461.  *
  462.  *    Reads characters using next_char() until encountering a separator, nl,
  463.  *    or end-of-file.  The returned value is the character which caused
  464.  *    reading to stop.  The following translations are done on the input:
  465.  *
  466.  *        ^X  goes to  ctrl-X (i.e. X & 037)
  467.  *        {\E,\n,\r,\b,\t,\f}  go to
  468.  *            {ESCAPE,newline,carriage-return,backspace,tab,formfeed}
  469.  *        {\^,\\}  go to  {carat,backslash}
  470.  *        \ddd (for ddd = up to three octal digits)  goes to the character ddd
  471.  *
  472.  *        \e == \E
  473.  *        \0 == \200
  474.  *
  475.  */
  476.  
  477. NCURSES_EXPORT(char)
  478. _nc_trans_string(char *ptr, char *last)
  479. {
  480.     int count = 0;
  481.     int number = 0;
  482.     int i, c;
  483.     chtype ch, last_ch = '\0';
  484.     bool ignored = FALSE;
  485.     bool long_warning = FALSE;
  486.  
  487.     while ((ch = c = next_char()) != (chtype) separator && c != EOF) {
  488.     if (ptr == (last - 1))
  489.         break;
  490.     if ((_nc_syntax == SYN_TERMCAP) && c == '\n')
  491.         break;
  492.     if (ch == '^' && last_ch != '%') {
  493.         ch = c = next_char();
  494.         if (c == EOF)
  495.         _nc_err_abort(MSG_NO_INPUTS);
  496.  
  497.         if (!(is7bits(ch) && isprint(ch))) {
  498.         _nc_warning("Illegal ^ character - %s", unctrl(ch));
  499.         }
  500.         if (ch == '?') {
  501.         *(ptr++) = '\177';
  502.         if (_nc_tracing)
  503.             _nc_warning("Allow ^? as synonym for \\177");
  504.         } else {
  505.         if ((ch &= 037) == 0)
  506.             ch = 128;
  507.         *(ptr++) = (char) (ch);
  508.         }
  509.     } else if (ch == '\\') {
  510.         ch = c = next_char();
  511.         if (c == EOF)
  512.         _nc_err_abort(MSG_NO_INPUTS);
  513.  
  514.         if (ch >= '0' && ch <= '7') {
  515.         number = ch - '0';
  516.         for (i = 0; i < 2; i++) {
  517.             ch = c = next_char();
  518.             if (c == EOF)
  519.             _nc_err_abort(MSG_NO_INPUTS);
  520.  
  521.             if (c < '0' || c > '7') {
  522.             if (isdigit(c)) {
  523.                 _nc_warning("Non-octal digit `%c' in \\ sequence", c);
  524.                 /* allow the digit; it'll do less harm */
  525.             } else {
  526.                 push_back((char) c);
  527.                 break;
  528.             }
  529.             }
  530.  
  531.             number = number * 8 + c - '0';
  532.         }
  533.  
  534.         if (number == 0)
  535.             number = 0200;
  536.         *(ptr++) = (char) number;
  537.         } else {
  538.         switch (c) {
  539.         case 'E':
  540.         case 'e':
  541.             *(ptr++) = '\033';
  542.             break;
  543.  
  544.         case 'a':
  545.             *(ptr++) = '\007';
  546.             break;
  547.  
  548.         case 'l':
  549.         case 'n':
  550.             *(ptr++) = '\n';
  551.             break;
  552.  
  553.         case 'r':
  554.             *(ptr++) = '\r';
  555.             break;
  556.  
  557.         case 'b':
  558.             *(ptr++) = '\010';
  559.             break;
  560.  
  561.         case 's':
  562.             *(ptr++) = ' ';
  563.             break;
  564.  
  565.         case 'f':
  566.             *(ptr++) = '\014';
  567.             break;
  568.  
  569.         case 't':
  570.             *(ptr++) = '\t';
  571.             break;
  572.  
  573.         case '\\':
  574.             *(ptr++) = '\\';
  575.             break;
  576.  
  577.         case '^':
  578.             *(ptr++) = '^';
  579.             break;
  580.  
  581.         case ',':
  582.             *(ptr++) = ',';
  583.             break;
  584.  
  585.         case ':':
  586.             *(ptr++) = ':';
  587.             break;
  588.  
  589.         case '\n':
  590.             continue;
  591.  
  592.         default:
  593.             _nc_warning("Illegal character %s in \\ sequence",
  594.                 unctrl(ch));
  595.             *(ptr++) = (char) ch;
  596.         }        /* endswitch (ch) */
  597.         }            /* endelse (ch < '0' ||  ch > '7') */
  598.     }
  599.     /* end else if (ch == '\\') */
  600.     else if (ch == '\n' && (_nc_syntax == SYN_TERMINFO)) {
  601.         /* newlines embedded in a terminfo string are ignored */
  602.         ignored = TRUE;
  603.     } else {
  604.         *(ptr++) = (char) ch;
  605.     }
  606.  
  607.     if (!ignored) {
  608.         last_ch = ch;
  609.         count++;
  610.     }
  611.     ignored = FALSE;
  612.  
  613.     if (count > MAXCAPLEN && !long_warning) {
  614.         _nc_warning("Very long string found.  Missing separator?");
  615.         long_warning = TRUE;
  616.     }
  617.     }                /* end while */
  618.  
  619.     *ptr = '\0';
  620.  
  621.     return (ch);
  622. }
  623.  
  624. /*
  625.  *    _nc_push_token()
  626.  *
  627.  *    Push a token of given type so that it will be reread by the next
  628.  *    get_token() call.
  629.  */
  630.  
  631. NCURSES_EXPORT(void)
  632. _nc_push_token(int tokclass)
  633. {
  634.     /*
  635.      * This implementation is kind of bogus, it will fail if we ever do more
  636.      * than one pushback at a time between get_token() calls.  It relies on the
  637.      * fact that _nc_curr_token is static storage that nothing but
  638.      * _nc_get_token() touches.
  639.      */
  640.     pushtype = tokclass;
  641.     if (pushname == 0)
  642.     pushname = typeMalloc(char, MAX_NAME_SIZE + 1);
  643.     _nc_get_type(pushname);
  644.  
  645.     DEBUG(3, ("pushing token: `%s', class %d",
  646.           _nc_curr_token.tk_name, pushtype));
  647. }
  648.  
  649. /*
  650.  * Panic mode error recovery - skip everything until a "ch" is found.
  651.  */
  652. NCURSES_EXPORT(void)
  653. _nc_panic_mode(char ch)
  654. {
  655.     int c;
  656.  
  657.     for (;;) {
  658.     c = next_char();
  659.     if (c == ch)
  660.         return;
  661.     if (c == EOF)
  662.         return;
  663.     }
  664. }
  665.  
  666. /*****************************************************************************
  667.  *
  668.  * Character-stream handling
  669.  *
  670.  *****************************************************************************/
  671.  
  672. #define LEXBUFSIZ    1024
  673.  
  674. static char *bufptr;        /* otherwise, the input buffer pointer */
  675. static char *bufstart;        /* start of buffer so we can compute offsets */
  676. static FILE *yyin;        /* scanner's input file descriptor */
  677.  
  678. /*
  679.  *    _nc_reset_input()
  680.  *
  681.  *    Resets the input-reading routines.  Used on initialization,
  682.  *    or after a seek has been done.  Exactly one argument must be
  683.  *    non-null.
  684.  */
  685.  
  686. NCURSES_EXPORT(void)
  687. _nc_reset_input(FILE * fp, char *buf)
  688. {
  689.     pushtype = NO_PUSHBACK;
  690.     if (pushname != 0)
  691.     pushname[0] = '\0';
  692.     yyin = fp;
  693.     bufstart = bufptr = buf;
  694.     _nc_curr_file_pos = 0L;
  695.     if (fp != 0)
  696.     _nc_curr_line = 0;
  697.     _nc_curr_col = 0;
  698. }
  699.  
  700. /*
  701.  *    int last_char()
  702.  *
  703.  *    Returns the final nonblank character on the current input buffer
  704.  */
  705. static int
  706. last_char(void)
  707. {
  708.     size_t len = strlen(bufptr);
  709.     while (len--) {
  710.     if (!isspace(UChar(bufptr[len])))
  711.         return bufptr[len];
  712.     }
  713.     return 0;
  714. }
  715.  
  716. /*
  717.  *    int next_char()
  718.  *
  719.  *    Returns the next character in the input stream.  Comments and leading
  720.  *    white space are stripped.
  721.  *
  722.  *    The global state variable 'firstcolumn' is set TRUE if the character
  723.  *    returned is from the first column of the input line.
  724.  *
  725.  *    The global variable _nc_curr_line is incremented for each new line.
  726.  *    The global variable _nc_curr_file_pos is set to the file offset of the
  727.  *    beginning of each line.
  728.  */
  729.  
  730. static int
  731. next_char(void)
  732. {
  733.     if (!yyin) {
  734.     /*
  735.      * An string with an embedded null will truncate the input.  This is
  736.      * intentional (we don't read binary files here).
  737.      */
  738.     if (*bufptr == '\0')
  739.         return (EOF);
  740.     if (*bufptr == '\n') {
  741.         _nc_curr_line++;
  742.         _nc_curr_col = 0;
  743.     }
  744.     } else if (!bufptr || !*bufptr) {
  745.     /*
  746.      * In theory this could be recoded to do its I/O one character at a
  747.      * time, saving the buffer space.  In practice, this turns out to be
  748.      * quite hard to get completely right.  Try it and see.  If you
  749.      * succeed, don't forget to hack push_back() correspondingly.
  750.      */
  751.     static char *result;
  752.     static size_t allocated;
  753.     size_t used;
  754.     size_t len;
  755.  
  756.     do {
  757.         bufstart = 0;
  758.         used = 0;
  759.         do {
  760.         if (used + (LEXBUFSIZ / 4) >= allocated) {
  761.             allocated += (allocated + LEXBUFSIZ);
  762.             result = typeRealloc(char, allocated, result);
  763.             if (result == 0)
  764.             return (EOF);
  765.         }
  766.         if (used == 0)
  767.             _nc_curr_file_pos = ftell(yyin);
  768.  
  769.         if (fgets(result + used, allocated - used, yyin) != NULL) {
  770.             bufstart = result;
  771.             if (used == 0) {
  772.             _nc_curr_line++;
  773.             _nc_curr_col = 0;
  774.             }
  775.         } else {
  776.             if (used != 0)
  777.             strcat(result, "\n");
  778.         }
  779.         if ((bufptr = bufstart) != 0) {
  780.             used = strlen(bufptr);
  781.             while (iswhite(*bufptr))
  782.             bufptr++;
  783.  
  784.             /*
  785.              * Treat a trailing <cr><lf> the same as a <newline> so we
  786.              * can read files on OS/2, etc.
  787.              */
  788.             if ((len = strlen(bufptr)) > 1) {
  789.             if (bufptr[len - 1] == '\n'
  790.                 && bufptr[len - 2] == '\r') {
  791.                 len--;
  792.                 bufptr[len - 1] = '\n';
  793.                 bufptr[len] = '\0';
  794.             }
  795.             }
  796.         } else {
  797.             return (EOF);
  798.         }
  799.         } while (bufptr[len - 1] != '\n');    /* complete a line */
  800.     } while (result[0] == '#');    /* ignore comments */
  801.     }
  802.  
  803.     first_column = (bufptr == bufstart);
  804.  
  805.     _nc_curr_col++;
  806.     return (*bufptr++);
  807. }
  808.  
  809. static void
  810. push_back(char c)
  811. /* push a character back onto the input stream */
  812. {
  813.     if (bufptr == bufstart)
  814.     _nc_syserr_abort("Can't backspace off beginning of line");
  815.     *--bufptr = c;
  816. }
  817.  
  818. static long
  819. stream_pos(void)
  820. /* return our current character position in the input stream */
  821. {
  822.     return (yyin ? ftell(yyin) : (bufptr ? bufptr - bufstart : 0));
  823. }
  824.  
  825. static bool
  826. end_of_stream(void)
  827. /* are we at end of input? */
  828. {
  829.     return ((yyin ? feof(yyin) : (bufptr && *bufptr == '\0'))
  830.         ? TRUE : FALSE);
  831. }
  832.